/* eslint-disable react/display-name */
import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import type { FC, MouseEvent, ReactNode } from 'react';
import ReactDOM from 'react-dom';
import type { Placement } from '@popperjs/core';
import { Popper } from 'react-popper';
import classNames from 'classnames';
import styles from './popper-flow.less';

export interface PopperProps extends React.DOMAttributes<HTMLElement> {
  /**
   * @description 应用主题名，默认为dark（暗黑色）
   * @default 'white'
   */
  theme?: 'white' | 'dark';

  /** 应为字符串或inline元素 */
  children?: ReactNode;

  /**
   * @description 组件顶层标签名
   * @default 'div'
   */
  tagName?: FC;

  /** 悬浮窗展示的内容 */
  tooltip?: ReactNode;

  /** 悬浮窗样式类名 */
  tooltipClassName?: string;

  /** 悬浮窗自定义样式 */
  tooltipStyle?: React.CSSProperties;

  /** 箭头样式类名 */
  arrowClassName?: string;

  /**
   * @description 悬浮窗显示延迟时间，(单位ms)
   * @default 150
   */
  showDelay?: number;

  /**
   * @description 悬浮窗隐藏延迟时间，(单位ms)
   * @default 150
   */
  hideDelay?: number;

  /**
   * @description 是否隐藏箭头
   * @default false
   */
  hideArrow?: boolean;

  /**
   * @description 悬浮窗相对位置
   * @default 'top'
   */
  placement?: Placement;

  container?: Element | ((target: Element) => Element);

  /** 滚动容器隐藏Tooltip，需要container传入scroll容器才能生效 */
  hideTooltipOnScroll?: boolean;

  /** 是否展示tooltip */
  showTooltip?: boolean;
}

const PopperFlow = forwardRef<HTMLDivElement, PopperProps>((props, _ref) => {
  const {
    theme = 'white',
    children,
    tagName: Tag = 'div',
    tooltip,
    tooltipClassName,
    tooltipStyle,
    arrowClassName,
    showDelay = 80,
    hideDelay = 80,
    hideArrow = false,
    placement = 'top',
    container = () => document.body,
    hideTooltipOnScroll = true,
    showTooltip = true,
    ...rest
  } = props;
  // #region 是否展示 tooltip 相关逻辑:
  const [_showToolTip, _setShowTooltip] = useState(false);
  const showTimer = useRef(0);
  const hideTimer = useRef(0);
  const hideDelayRef = useRef(0);
  const showDelayRef = useRef(0);
  useEffect(() => {
    hideDelayRef.current = hideDelay;
    showDelayRef.current = showDelay;
  }, [hideDelay, showDelay]);

  useEffect(() => {
    return () => {
      clearTimeout(hideTimer.current);
      clearTimeout(showTimer.current);
    };
  }, []);

  useEffect(() => {
    if (showTooltip) {
      clearTimeout(hideTimer.current);
      showTimer.current = window.setTimeout(() => {
        _setShowTooltip(true);
      }, showDelayRef.current || 0);
    } else {
      clearTimeout(showTimer.current);
      hideTimer.current = window.setTimeout(() => {
        _setShowTooltip(false);
      }, hideDelayRef.current || 0);
    }
  }, [showTooltip]);
  // #endregion

  const targetNode = useRef<HTMLDivElement | undefined>();
  const refFn = (ele: HTMLDivElement | null) => {
    if (typeof _ref === 'function') {
      _ref(ele);
    } else if (_ref) {
      _ref.current = ele;
    }
    targetNode.current = ele ?? undefined;
  };

  const getContainer = useMemo(
    () => (typeof container === 'function' ? (e?: Element) => (!e ? document.body : container(e)) : () => container),
    [container]
  );

  useEffect(() => {
    const ele = getContainer(targetNode.current);
    if (hideTooltipOnScroll && _showToolTip && ele) {
      const hideToolTip = () => {
        _setShowTooltip(false);
      };
      ele.addEventListener('scroll', hideToolTip, { once: true });
      return () => {
        ele.removeEventListener('scroll', hideToolTip);
      };
    }
  }, [hideTooltipOnScroll, _showToolTip, getContainer]);
  const isDark = theme === 'dark';
  const handleMouseEvent = (e: MouseEvent) => {
    e.stopPropagation();
  };

  return (
    <Tag {...rest} ref={refFn}>
      {children}
      {_showToolTip && (
        <Popper
          referenceElement={targetNode.current}
          placement={placement}
          // 参考：https://popper.js.org/docs/v2/modifiers/prevent-overflow/
          modifiers={[
            {
              name: 'preventOverflow',
            },
          ]}
        >
          {(tooltipProps) => {
            const { ref, style, arrowProps, placement: tooltipPlacement } = tooltipProps;

            return ReactDOM.createPortal(
              <div
                ref={ref}
                data-qtrade-popper={tooltipPlacement}
                className={classNames(tooltipClassName, {
                  [styles['tooltip-dark']]: isDark,
                })}
                style={tooltipStyle ? { ...style, ...tooltipStyle } : style}
                onClick={handleMouseEvent}
                onDoubleClick={handleMouseEvent}
                onContextMenu={handleMouseEvent}
                onMouseDown={handleMouseEvent}
                onMouseUp={handleMouseEvent}
              >
                {tooltip}
                {!hideArrow && (
                  <div
                    ref={arrowProps.ref}
                    data-qtrade-popper-arrow={tooltipPlacement}
                    className={classNames(arrowClassName, { [styles['tooltip-arrow-dark']]: isDark })}
                    style={arrowProps.style}
                  />
                )}
              </div>,
              getContainer(targetNode.current)
            ) as unknown as React.ReactPortal;
          }}
        </Popper>
      )}
    </Tag>
  );
});
export default PopperFlow;
